home *** CD-ROM | disk | FTP | other *** search
- /* info -- a stand-alone Info program
-
- Copyright (C) 1987 Free Software Foundation, Inc.
-
- This file is part of GNU Info.
-
- GNU Info is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY. No author or distributor accepts
- responsibility to anyone for the consequences of using it or for
- whether it serves any particular purpose or works at all, unless he
- says so in writing. Refer to the GNU Emacs General Public License
- for full details.
-
- Everyone is granted permission to copy, modify and redistribute
- GNU Info, but only under the conditions described in the GNU Emacs
- General Public License. A copy of this license is supposed to
- have been given to you along with GNU Emacs so you can know your
- rights and responsibilities. It should be in a file named COPYING.
- Among other things, the copyright notice and this notice must be
- preserved on all copies. */
-
- /* MS-DOS port (c) 1990 by Thorsten Ohl, td12@ddagsi3.bitnet
- This port is also distributed under the terms of the
- GNU General Public License as published by the
- Free Software Foundation.
-
- Please note that this file is not identical to the
- original GNU release, you should have received this
- code as patch to the official release.
-
- $Header: e:/gnu/info/RCS/info.c 0.1.1.2 90/10/26 20:05:12 tho Exp $
- */
-
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <stdio.h>
- #ifdef MSDOS
- #include <process.h>
- #include <conio.h>
- #undef getc
- #define getc(stream) pc_getc () /* read from console */
- #else /* not MSDOS */
- #include <sgtty.h>
- #endif /* not MSDOS */
- #include <signal.h>
- #ifndef MSDOS
- #include <pwd.h>
- #endif
-
- #include "getopt.h"
-
- #ifdef SYSV
- struct passwd *getpwnam ();
- #include <fcntl.h>
- #define bcopy(source, dest, count) memcpy(dest, source, count)
- #define index strchr
- #else
- #include <sys/file.h>
- #endif
-
- #ifndef DEFAULT_INFOPATH
- #define DEFAULT_INFOPATH ".:/usr/gnu/info:/usr/local/emacs/info:/usr/local/lib/emacs/info"
- #endif
-
-
- #ifdef MSDOS
- #define LONG long
- #else /* not MSDOS */
- #define LONG int
- #endif /* not MSDOS */
-
- typedef int boolean;
- #define true 1
- #define false 0
- #define UNIX
-
- typedef struct nodeinfo
- {
- char *filename;
- char *nodename;
- LONG pagetop;
- LONG nodetop;
- struct nodeinfo *next;
- } NODEINFO;
-
- typedef struct indirectinfo
- {
- char *filename;
- LONG first_byte;
- } INDIRECT_INFO;
-
- typedef int Function ();
-
- #define PROJECT_NAME "GNU Info"
-
- #define barf(msg) fprintf(stderr, "%s\n", msg)
-
- /* Some character stuff. */
- #define control_character_threshold 0x020 /* smaller than this is control */
- #define meta_character_threshold 0x07f /* larger than this is Meta. */
- #define control_character_bit 0x40 /* 0x000000, must be off. */
- #define meta_character_bit 0x080/* x0000000, must be on. */
-
- #define info_separator_char '\037'
- #define start_of_node_string "\037"
-
- #ifdef CTRL
- #undef CTRL
- #endif
-
- #define CTRL(c) ((c) & (~control_character_bit))
- #define META(c) ((c) | meta_character_bit)
-
- #define UNMETA(c) ((c) & (~meta_character_bit))
- #define UNCTRL(c) to_upper(((c)|control_character_bit))
-
- #ifndef to_upper
- #define to_upper(c) (((c) < 'a' || (c) > 'z') ? c : c-32)
- #define to_lower(c) (((c) < 'A' || (c) > 'Z') ? c : c+32)
- #endif
-
- #define CTRL_P(c) ((c) < control_character_threshold)
- #define META_P(c) ((c) > meta_character_threshold)
-
- #define NEWLINE '\n'
- #define RETURN CTRL('M')
- #define DELETE 0x07f
- #define TAB '\t'
- #define ABORT_CHAR CTRL('G')
- #define PAGE CTRL('L')
- #define SPACE 0x020
- #define ESC CTRL('[')
- #define control_display_prefix '^'
-
- #define TAG_TABLE_END_STRING "\037\nEND TAG TABLE"
- #define TAG_TABLE_BEG_STRING "\nTAG TABLE:\n"
- #define NODE_ID "Node:"
- #define FILENAME_LEN 256
- #define NODENAME_LEN 256
- #define STRING_SIZE 256
- #define nodeend_sequence "\n\037"
-
- /* All right, some windows stuff. */
-
- typedef struct
- {
- int left, top, right, bottom;
- int ch, cv;
- } WINDOW;
-
- typedef struct _wind_list
- {
- int left, top, right, bottom;
- int ch, cv;
- struct _wind_list *next_window;
- } WINDOW_LIST;
-
- /* Stuff that everyone has to know about. */
- extern WINDOW the_window;
- extern WINDOW echo_area;
- extern LONG pagetop, nodetop, nodebot;
- extern int nodelines;
- extern char current_info_file[], current_info_node[], last_loaded_info_file[];
- extern char *info_file;
-
- #ifdef MSDOS
-
- #define VOID void
- #if defined (_MSC_VER) && (_MSC_VER >= 600)
- #define CDECL _cdecl
- #else
- #define CDECL cdecl
- #endif
-
- #include <stdlib.h>
- #include <string.h>
- #include <stdarg.h>
- #include <ctype.h>
- #include <assert.h>
-
- #include "pc_term.h"
-
- static void replace_loosing_sscanf (char *buf, char *str, long *np);
-
- char *extract_colon_unit (char *string, int *index);
- char *make_temp_filename (char *starter);
- char *opsys_filename (char *partial);
- char *xmalloc (int bytes);
- void add_completion (char *identifier, char *data);
- void advance (int amount);
- LONG back_lines (int count, LONG starting_pos);
- void begin_info_session (void);
- int blink_cursor (void);
- int brians_error (void);
- int build_menu (void);
- int build_notes (void);
- int character_output_function (char character);
- void charout (int character);
- void clean_up (char *string);
- void clear_echo_area (void);
- void clear_eol (void);
- void clear_eop (void);
- void clear_eop_slowly (void);
- void close_echo_area (void);
- void close_typeout (void);
- int complete (char *text, struct completion_entry *list);
- int deletefile (char *filename);
- void ding (void);
- void display_carefully (int character);
- int CDECL display_error (char *format_string, ...);
- void display_page (void);
- int display_width (int character, int hpos);
- int dump_current_node (char *filename);
- int extract_field (char *field_name, char *nodename, LONG offset);
- int file_error (char *file);
- LONG find_footnote_ref (LONG from);
- int find_menu_node (char *string, char *nodename);
- LONG find_node_in_file (char *nodename, LONG offset);
- LONG find_node_in_tag_table (char *nodename, LONG offset);
- LONG find_node_start (LONG start);
- int find_note_node (char *string, char *nodename);
- LONG forward_lines (int count, LONG starting_pos);
- void free_completion_list (void);
- void generic_page_display (void);
- int get_info_file (char *filename, int remember_name);
- int get_menu (int item);
- int get_node (char *filename, char *nodename, int popping);
- void get_node_extent (void);
- int get_y_or_n_p (void);
- void goto_xy (int xpos, int ypos);
- void help_possible_completions (char *text);
- void help_use_info (void);
- void indent_to (int screen_column);
- void CDECL info_signal_handler (int sig);
- void init_completer (void);
- void init_echo_area (int left, int top, int right, int bottom);
- void init_terminal_io (void);
- void install_signals (void);
- void I_goto_xy (int xpos, int ypos);
- int looking_at (char *string, LONG pointer);
- void CDECL main (int argc, char **argv);
- void make_modeline (void);
- void new_echo_area (void);
- int next_node (void);
- int next_page (void);
- void open_typeout (void);
- int pop_node (char *filename, char *nodename, LONG *nodetop, LONG *pagetop);
- void pop_window (void);
- int prev_node (void);
- int prev_page (void);
- void print_cr (void);
- void CDECL print_string (char *string, ...);
- void print_tab (void);
- int push_node (char *filename, char *nodename, LONG page_position,
- LONG node_position);
- void push_window (void);
- int readline (char *prompt, char *line, int maxchars, int completing);
- void remember_completion (struct completion_entry *pointer);
- void restore_io (void);
- int scan_list (char *string, char *nodename);
- LONG search_backward (char *string, LONG starting_pos);
- LONG search_forward (char *string, LONG starting_pos);
- void set_search_constraints (char *buffer, LONG extent);
- void set_window (WINDOW *window);
- LONG skip_whitespace (LONG offset);
- LONG skip_whitespace_and_cr (LONG offset);
- LONG string_in_line (char *string, LONG pointer);
- void toploop (void);
- LONG to_beg_line (LONG from);
- LONG to_end_line (LONG from);
- int try_complete (char *text);
- int up_node (void);
- void usage (void);
- int CDECL with_output_to_window (WINDOW *window, int (*function) (), ...);
- struct completion_entry *reverse_list (struct completion_entry *list);
-
- #else /* not MSDOS */
-
- #define VOID
- #define CDECL
-
- char *malloc ();
- char *realloc ();
- char *xmalloc ();
- char *xrealloc ();
- char *getenv ();
- char *index ();
- char *strcat ();
- char *strcpy ();
-
- #endif /* not MSDOS */
-
- boolean build_menu ();
- boolean find_menu_node ();
- char *opsys_filename ();
-
- #define MAX_INDIRECT_FILES 100 /* a crock, this should be done in a different way. */
- NODEINFO *Info_History = NULL; /* the info history list. */
- INDIRECT_INFO indirect_list[MAX_INDIRECT_FILES]; /* ?Can't have more than xx files in the indirect list? */
-
- char current_info_file[FILENAME_LEN]; /* the filename of the currently loaded info file. */
- char current_info_node[NODENAME_LEN]; /* the nodename of the node the user is looking at. */
- char last_loaded_info_file[FILENAME_LEN]; /* the last file actually loaded. Not the same as current info file. */
- LONG nodetop, nodebot; /* offsets in info_file of top and bottom of current_info_node. */
- int nodelines; /* number of lines in this node. */
-
- char *info_file = NULL; /* buffer for the info file. */
- LONG info_buffer_len; /* length of the above buffer. */
-
- char *tag_table = NULL; /* Pointer to the start of a tag table, or NULL to show none. */
- LONG tag_buffer_len; /* length of the above buffer. */
-
- boolean indirect = false; /* If true, the tag table is indirect. */
- LONG indirect_top;
- LONG pagetop; /* offset in the buffer of the current pagetop. */
-
-
- /* If non-NULL, this is a colon separated list of directories to search
- for a specific info file. The user places this variable into his or
- her environment. */
- char *infopath = NULL;
-
- char dumpfile[FILENAME_LEN]; /* If filled, the name of a file to write to. */
-
- /* **************************************************************** */
- /* */
- /* Getting Started. */
- /* */
- /* **************************************************************** */
-
- /* Begin the Info session. */
-
- /* Global is on until we are out of trouble. */
- int totally_inhibit_errors = 1;
-
- struct option long_options[] =
- {
- {"directory", 1, 0, 'd'},
- {"node", 1, 0, 'n'},
- {"file", 1, 0, 'f'},
- {"output", 1, 0, 'o'},
- {NULL, 0, NULL, 0}
- };
-
- #define savestring(x) strcpy (xmalloc (1 + strlen (x)), (x))
-
- VOID CDECL
- main (argc, argv)
- int argc;
- char **argv;
- {
- int c;
- int ind;
- char filename[FILENAME_LEN];
- char nodename[NODENAME_LEN];
- char *env_infopath = getenv ("INFOPATH");
-
- nodename[0] = filename[0] = '\0';
-
- if (env_infopath && *env_infopath)
- infopath = savestring (env_infopath);
- else
- infopath = savestring (DEFAULT_INFOPATH);
-
- while ((c = getopt_long (argc, argv, "d:n:f:o:", long_options, &ind)) != EOF)
- {
- if (c == 0 && long_options[ind].flag == 0)
- c = long_options[ind].val;
- switch (c)
- {
- case 0:
- break;
-
- case 'd':
- free (infopath);
- infopath = savestring (optarg);
- break;
-
- case 'n':
- strncpy (nodename, optarg, FILENAME_LEN);
- break;
-
- case 'f':
- strncpy (filename, optarg, FILENAME_LEN);
- break;
-
- case 'o':
- strncpy (dumpfile, optarg, FILENAME_LEN);
- break;
-
- default:
- usage ();
- }
- }
-
- /* Okay, flags are parsed. Get possible Info menuname. */
-
- /* Start with DIR or whatever was specified. */
- if (!get_node (filename, nodename, false)
- && !get_node ((char *) NULL, (char *) NULL, true))
- {
- if (!*filename)
- strcpy (filename, "dir");
-
- fprintf (stderr, "%s: Cannot find \"%s\", anywhere along the\n",
- argv[0], filename);
- fprintf (stderr, "search path of \"%s\".\n", infopath);
-
- exit (1);
- }
- totally_inhibit_errors = 0;
- get_node (filename, nodename, false);
-
- if (optind != argc)
- {
- putchar ('\n');
-
- while (optind != argc)
- {
- if (build_menu () &&
- find_menu_node (argv[optind], nodename) &&
- get_node ((char *) NULL, nodename, false))
- {
- #ifdef NEVER
- /* RMS says not to type this stuff out because he expects programs
- to call Info instead of interactive users. */
- printf ("%s.. ",argv[optind]);
- fflush (stdout);
- #endif
- optind++;
- }
- else
- break;
- }
- }
- begin_info_session ();
- }
-
- VOID
- usage ()
- {
- fprintf (stderr,
- "Usage: info [-d dir-path] [-f info-file] [-n node-name] [-o output-file]\n");
- fprintf (stderr,
- " [+directory dir-path] [+file info-file] [+node node-name]\n");
- fprintf (stderr,
- " [+output output-file] [menu-selection...]\n");
- exit (1);
- }
-
- #ifdef SIGTSTP
- Function *old_stop; /* last value of SIGSTOP. */
- Function *old_cont; /* last value of SIGCONT. */
- #endif
-
- #ifdef SIGWINCH
- Function *old_winch; /* last value of SIGWINCH. */
- #endif
-
- VOID
- begin_info_session ()
- {
- /* Start using Info. */
- #ifndef MSDOS
- int info_signal_handler ();
- #endif /* not MSDOS */
-
- /* If the user just wants to dump the node, then do that. */
- if (dumpfile[0])
- {
- dump_current_node (dumpfile);
- exit (0);
- }
-
- init_terminal_io ();
-
- /* Install handlers for restoring/breaking the screen. */
-
- install_signals ();
- new_echo_area ();
-
- print_string ("Welcome to Info! Type \"?\" for help. ");
- close_echo_area ();
- toploop ();
- restore_io ();
- }
-
- VOID CDECL
- info_signal_handler (sig)
- int sig;
- {
- /* Do the right thing with this signal. */
-
- switch (sig)
- {
-
- #ifdef SIGTSTP
- case SIGTSTP:
- restore_io ();
- signal (SIGTSTP, old_stop);
- kill (getpid (), SIGSTOP); /* Some weenie says this is the right thing. */
- break;
-
- case SIGCONT:
- /* Try to win some more. Reset IO state, and stuff
- like that. */
-
- clear_screen ();
- display_page ();
- goto_xy (the_window.ch, the_window.cv);
- opsys_init_terminal ();
- signal (SIGTSTP, info_signal_handler);
- break;
- #endif /* SIGTSTP */
-
- #ifdef SIGWINCH
- case SIGWINCH:
- /* Window has changed. Get the size of the new window, and rebuild our
- window chain. */
- {
- extern char *widest_line;
- extern WINDOW terminal_window;
- extern WINDOW_LIST *window_stack;
- extern int terminal_lines, terminal_rows;
- int delta_width, delta_height, right, bottom;
-
- get_terminal_info ();
- right = get_term_width ();
- bottom = get_term_height ();
-
- delta_width = right - terminal_rows;
- delta_height = bottom - terminal_lines;
-
- terminal_rows = right;
- terminal_lines = bottom - 3;
- push_window ();
- free (widest_line);
- widest_line = (char *) xmalloc (right);
- set_window (&terminal_window);
- push_window ();
-
- /* Make the new window. Map over all windows in window list. */
- {
- WINDOW_LIST *wind = window_stack;
- extern WINDOW modeline_window;
-
- while (wind != (WINDOW_LIST *) NULL)
- {
- adjust_wind ((WINDOW *) wind, delta_width, delta_height);
- wind = wind->next_window;
- }
-
- /* Adjust the other windows that we know about. */
-
- adjust_wind (&terminal_window, delta_width, delta_height);
- adjust_wind (&echo_area, delta_width, delta_height);
- adjust_wind (&modeline_window, delta_width, delta_height);
- }
- pop_window ();
- clear_screen ();
- with_output_to_window (&terminal_window, display_page);
- pop_window ();
- }
- break;
- #endif /* SIGWINCH */
- case SIGINT:
- restore_io ();
- exit (1);
- break;
- }
- }
-
- VOID
- install_signals ()
- {
- #ifdef SIGTSTP
- old_stop = (Function *) signal (SIGTSTP, info_signal_handler);
- old_cont = (Function *) signal (SIGCONT, info_signal_handler);
- #endif /* SIGTSTP */
-
- #ifdef SIGWINCH
- old_winch = (Function *) signal (SIGWINCH, info_signal_handler);
- #endif
-
- signal (SIGINT, info_signal_handler);
- }
-
- #ifdef SIGWINCH
- adjust_wind (wind, delta_width, delta_height)
- WINDOW *wind;
- int delta_width, delta_height;
- {
- wind->right += delta_width;
- wind->bottom += delta_height;
- if (wind->top)
- wind->top += delta_height;
- if (wind->left)
- wind->left += delta_width;
- }
- #endif /* SIGWINCH */
-
- /* **************************************************************** */
- /* */
- /* Completing Things */
- /* */
- /* **************************************************************** */
- typedef struct completion_entry
- {
- char *identifier;
- char *data;
- struct completion_entry *next;
- } COMP_ENTRY;
-
- /* The linked list of COMP_ENTRY structures that you create. */
- COMP_ENTRY *completion_list = (COMP_ENTRY *) NULL;
-
- /* The vector of COMP_ENTRY pointers that COMPLETE returns. */
- COMP_ENTRY **completions = NULL;
-
- /* The number of elements in the above vector. */
- int completion_count;
-
- /* Initial size of COMPLETIONS array. */
- #define INITIAL_COMPLETIONS_CORE_SIZE 200
-
- /* Current size of the completion array in core. */
- int completions_core_size = 0;
-
- /* Ease the typing task. Another name for the I'th IDENTIFIER of COMPLETIONS. */
- #define completion_id(i) ((completions[(i)])->identifier)
-
- /* The number of completions that can be present before the help
- function starts asking you about whether it should print them
- all or not. */
- int completion_query_threshold = 100;
-
- VOID
- free_completion_list ()
- {
- COMP_ENTRY *temp;
- while (completion_list)
- {
- temp = completion_list;
- if (completion_list->identifier)
- free (completion_list->identifier);
- if (completion_list->data)
- free (completion_list->data);
- completion_list = completion_list->next;
- free (temp);
- }
- }
-
- /* Add a single completion to COMPLETION_LIST.
- IDENTIFIER is the string that the user should type.
- DATA should just be a pointer to some random data that you wish to
- have associated with the identifier, but I'm too stupid for that, so
- it must be a string as well. This allocates the space for the strings
- so you don't necessarily have to. */
- VOID
- add_completion (identifier, data)
- char *identifier, *data;
- {
- COMP_ENTRY *temp = (COMP_ENTRY *) xmalloc (sizeof (COMP_ENTRY));
- temp->identifier = (char *) xmalloc (strlen (identifier) + 1);
- strcpy (temp->identifier, identifier);
- temp->data = (char *) xmalloc (strlen (data) + 1);
- strcpy (temp->data, data);
- temp->next = completion_list;
- completion_list = temp;
- }
-
- /* Function for reading a line. Supports completion on COMPLETION_LIST
- if you pass COMPLETING as true. Prompt is either a prompt or NULL,
- LINE is the place to store the characters that are read. LINE may start
- out already containing some characters; if so, they are printed. MAXCHARS
- tells how many characters can fit in the buffer at LINE. READLINE returns
- FALSE if the user types the abort character. LINE is returned with a '\0'
- at the end, not a '\n'. */
- boolean
- readline (prompt, line, maxchars, completing)
- char *prompt, *line;
- int maxchars;
- boolean completing;
- {
- int character;
- int readline_ch, readline_cv;
- int current_len = strlen (line);
- int meta_flag = 0;
-
- new_echo_area ();
- if (prompt)
- print_string (prompt);
- readline_ch = the_window.ch;
- readline_cv = the_window.cv;
-
- print_string (line);
-
- while (true)
- {
-
- line[current_len] = '\0';
- goto_xy (readline_ch, readline_cv);
- print_string (line);
- clear_eol ();
-
- character = blink_cursor ();
- if (meta_flag)
- {
- character = META (character);
- meta_flag = 0;
- }
-
- if (META_P (character))
- character = META (to_upper (UNMETA (character)));
-
- switch (character)
- {
-
- case EOF:
- character = '\n';
-
- case ESC:
- meta_flag++;
- break;
-
- case META (DELETE):
- case CTRL ('W'):
- while (current_len && line[current_len] == SPACE)
- current_len--;
- if (!current_len)
- break;
- while (current_len && line[current_len] != SPACE)
- current_len--;
- break;
-
- case CTRL ('U'):
- current_len = 0;
- break;
-
- case '\b':
- case 0x07f:
- if (current_len)
- current_len--;
- else
- ding ();
- break;
-
- case '\n':
- case '\r':
- if (completing)
- {
- extern int completion_count;
- try_complete (line);
- if (completion_count >= 1)
- {
- close_echo_area ();
- return (true);
- }
- else
- {
- current_len = strlen (line);
- break;
- }
- }
- else
- {
- close_echo_area ();
- return (true);
- }
-
- case ABORT_CHAR:
- ding ();
- if (current_len)
- {
- current_len = 0;
- }
- else
- {
- close_echo_area ();
- clear_echo_area ();
- return (false);
- }
- break;
-
- case ' ':
- case '\t':
- case '?':
- if (completing)
- {
- extern int completion_count;
- if (character == '?')
- {
- help_possible_completions (line);
- break;
- }
- else
- {
- char temp_line[NODENAME_LEN];
- strcpy (temp_line, line);
- try_complete (line);
- if (completion_count != 1 && character == SPACE)
- {
- if (strcmp (temp_line, line) == 0)
- {
- line[current_len] = SPACE;
- line[current_len + 1] = '\0';
- strcpy (temp_line, line);
- try_complete (line);
- if (completion_count == 0)
- {
- line[current_len] = '\0';
- ding ();
- }
- }
- }
- current_len = strlen (line);
- if (completion_count == 0)
- ding ();
- break;
- }
- }
- /* Do *NOT* put anything in-between the completing cases and
- the default: case. No. */
- default:
- if (!CTRL_P (character) && !META_P (character) &&
- current_len < maxchars)
- {
- line[current_len++] = character;
- }
- else
- ding ();
- }
- }
- }
-
- VOID
- init_completer ()
- {
- /* Initialize whatever the completer is using. */
- if (completions_core_size != INITIAL_COMPLETIONS_CORE_SIZE)
- {
- if (completions)
- free (completions);
- completions = (COMP_ENTRY **) xmalloc ((sizeof (COMP_ENTRY *)) *
- (completions_core_size = INITIAL_COMPLETIONS_CORE_SIZE));
- }
- completion_count = 0;
- }
-
- /* Reverse the completion list passed in LIST, and
- return a pointer to the new head. */
- COMP_ENTRY *
- reverse_list (list)
- COMP_ENTRY *list;
- {
- COMP_ENTRY *next;
- COMP_ENTRY *prev = (COMP_ENTRY *) NULL;
-
- while (list)
- {
- next = list->next;
- list->next = prev;
- prev = list;
- list = next;
- }
- return (prev);
- }
-
- VOID
- remember_completion (pointer)
- COMP_ENTRY *pointer;
- {
- if (completion_count == completions_core_size)
- {
- COMP_ENTRY **temp = (COMP_ENTRY **) realloc (completions,
- ((sizeof (COMP_ENTRY *)) * (completions_core_size += INITIAL_COMPLETIONS_CORE_SIZE)));
- if (!temp)
- {
- display_error ("Too many completions (~d)! Out of core!", completion_count);
- return;
- }
- else
- completions = temp;
- }
- completions[completion_count++] = pointer;
- }
-
- boolean
- complete (text, list)
- char *text;
- COMP_ENTRY *list;
- {
- /* Complete TEXT from identifiers in LIST. Place the resultant
- completions in COMPLETIONS, and the number of completions in
- COMPLETION_COUNT. Modify TEXT to contain the least common
- denominator of all the completions found. */
-
- int low_match, i, index;
- int string_length = strlen (text);
-
- init_completer ();
- low_match = 1000; /* some large number. */
-
- while (list)
- {
- if (strnicmp (text, list->identifier, string_length) == 0)
- remember_completion (list);
- list = list->next;
- }
-
- if (completion_count == 0)
- return (false);
-
- if (completion_count == 1)
- { /* One completion */
- strcpy (text, completion_id (0));
- return (true);
- }
-
- /* Else find the least common denominator */
-
- index = 1;
-
- while (index < completion_count)
- {
- int c1, c2;
- for (i = 0;
- (c1 = to_lower (completion_id (index - 1)[i])) &&
- (c2 = to_lower (completion_id (index)[i]));
- i++)
- if (c1 != c2)
- break;
-
- if (low_match > i)
- low_match = i;
- index++;
- }
-
- strncpy (text, completion_id (0), low_match);
- text[low_match] = '\0';
- return (true);
- }
-
- /* Complete TEXT from the completion structures in COMPLETION_LIST. */
- boolean
- try_complete (text)
- char *text;
- {
- return (complete (text, completion_list));
- }
-
- VOID
- help_possible_completions (text)
- char *text;
- {
- /* The function that prints out the possible completions. */
-
- char temp_string[2000];
-
- goto_xy (the_window.left, the_window.top);
- strcpy (temp_string, text);
- try_complete (temp_string);
-
- open_typeout ();
-
- if (completion_count == 0)
- {
- print_string ("There are no possible completions.\n");
- goto print_done;
- }
-
- if (completion_count == 1)
- {
- print_string ("The only possible completion of what you have typed is:\n\n");
- #ifdef MSDOS /* well, that was simply wrong... */
- print_string (completion_id (0));
- #else /* not MSDOS */
- print_string (completions[0]);
- #endif /* not MSDOS */
- goto print_done;
- }
-
- if (completion_count >= completion_query_threshold)
- {
- print_string ("\nThere are %d completions. Do you really want to see them all", completion_count);
- if (!get_y_or_n_p ())
- return;
- }
-
- print_string ("\nThe %d completions of what you have typed are:\n\n", completion_count);
-
- {
- int index = 0;
- int counter = 0;
- int columns = (the_window.right - the_window.left) / 30;
-
- while (index < completion_count)
- {
- if (counter == columns)
- {
- charout ('\n');
- counter = 0;
- }
- indent_to (counter * 30);
- print_string (completion_id (index));
- counter++;
- index++;
- }
- }
-
- print_done:
- print_string ("\n\nDone.\n-----------------\n");
- close_typeout ();
- }
-
- /* **************************************************************** */
- /* */
- /* Getting Nodes */
- /* */
- /* **************************************************************** */
-
- /* A node name looks like:
- Node: nodename with spaces but not a comma,
- or Node: (filename-containing-node)node-within-file
- or Node: (filename)
-
- The latter case implies a nodename of "Top". All files are
- supposed to have one.
-
- Lastly, the nodename specified could be "*", which specifies the
- entire file. */
-
- /* Load FILENAME. If REMEMBER_NAME is true, then remember the
- loaded filename in CURRENT_INFO_FILE. In either case, remember
- the name of this file in LAST_LOADED_INFO_FILE. */
- boolean
- get_info_file (filename, remember_name)
- char *filename;
- boolean remember_name;
- {
- FILE *input_stream;
- struct stat file_info;
- LONG pointer;
- int result;
- char tempname[FILENAME_LEN];
-
- /* Get real filename. */
- strcpy (tempname, opsys_filename (filename));
-
- /* See if the file exists. */
- if ((result = stat (tempname, &file_info)) != 0)
- {
- /* Try again, this time with the name in lower-case. */
- char temp_tempname[FILENAME_LEN];
- int i;
-
- for (i = 0; temp_tempname[i] = to_lower (tempname[i]); i++);
- /* Get real filename again. AMS */
- strcpy (temp_tempname, opsys_filename (temp_tempname));
-
- result = stat (temp_tempname, &file_info);
- if (!result)
- strcpy (tempname, temp_tempname);
- }
-
- /* See if this file is the last loaded one. */
- if (!result && (strcmp (last_loaded_info_file, tempname) == 0))
- {
- return (true); /* Okay, the file is loaded. */
- }
-
- /* Now try to open the file. */
- if (result || (input_stream = fopen (tempname, "r")) == NULL)
- {
- return (file_error (tempname));
- }
-
- /* If we already have a file loaded, then free it first. */
- if (info_file)
- {
- free (info_file);
-
- if (!indirect)
- {
- /* Then the tag table is also no longer valid. */
- tag_table = (char *) NULL;
- }
- }
-
- /* Read the contents of the file into a new buffer. */
-
- #ifdef MSDOS /* Fix this! allow files > 64k */
- assert (file_info.st_size < (1L<<16));
- info_file = (char *) xmalloc ((size_t) file_info.st_size);
- info_buffer_len
- = fread (info_file, 1, (size_t) file_info.st_size, input_stream);
- #else /* not MSDOS */
- info_file = (char *) xmalloc (info_buffer_len = file_info.st_size);
- fread (info_file, 1, info_buffer_len, input_stream);
- #endif /* not MSDOS */
- fclose (input_stream);
- strcpy (last_loaded_info_file, tempname);
- if (remember_name)
- {
- strcpy (current_info_file, tempname);
- if (indirect)
- {
- int index;
- indirect = false;
- free (tag_table);
- for (index = 0; index < MAX_INDIRECT_FILES &&
- indirect_list[index].filename != (char *) NULL;
- index++)
- {
- free (indirect_list[index].filename);
- indirect_list[index].filename = (char *) NULL;
- }
- }
- }
- else
- return (true);
-
- /* The file has been read, and we don't know anything about it.
- Find out if it contains a tag table. */
-
- tag_table = NULL; /* assume none. */
- indirect = false;
- tag_buffer_len = 0;
-
- set_search_constraints (info_file, info_buffer_len);
-
- /* Go to the last few lines in the file. */
- pointer = back_lines (8, info_buffer_len);
- pointer = search_forward (TAG_TABLE_END_STRING, pointer);
-
- if (pointer > -1)
- {
- /* Then there is a tag table. Find the start of it,
- and remember that. */
- pointer = search_backward (TAG_TABLE_BEG_STRING, pointer);
-
- /* Handle error for malformed info file. */
- if (pointer < 0)
- display_error ("Start of tag table not found!");
- else
- {
- /* No problem. If this file is an indirect file, then the contents
- of the tag table must remain in RAM the entire time. Otherwise,
- we can flush the tag table with the file when the file is flushed.
- So, if indirect, remember that, and copy the table to another
- place.*/
-
- LONG indirect_check = forward_lines (2, pointer);
-
- tag_table = info_file + pointer;
- tag_buffer_len = info_buffer_len - pointer;
-
- /* Shorten the search constraints. */
- info_buffer_len = pointer;
-
- if (looking_at ("(Indirect)\n", indirect_check))
- {
-
- /* We have to find the start of the indirect file's
- information. */
-
- #ifdef MSDOS
- assert (tag_buffer_len < (1L<<16));
- tag_table = (char *) xmalloc ((size_t) tag_buffer_len);
- bcopy (&info_file[indirect_check], tag_table,
- (size_t) tag_buffer_len);
- #else /* not MSDOS */
- tag_table = (char *) xmalloc (tag_buffer_len);
- bcopy (&info_file[indirect_check], tag_table, tag_buffer_len);
- #endif /* not MSDOS */
-
- /* Find the list of filenames. */
- indirect_top = search_backward ("Indirect:\n", indirect_check);
- if (indirect_top < 0)
- {
- free (tag_table);
- tag_table = (char *) NULL;
- display_error ("Start of INDIRECT tag table not found!");
- return (false);
- }
-
- /* Remember the filenames, and their byte offsets. */
- {
- /* Index into the filename/offsets array. */
- int index = 0;
- char temp_filename[FILENAME_LEN];
- LONG temp_first_byte;
-
- info_buffer_len = indirect_top;
-
- /* For each line, scan the info into globals. Then save
- the information in the INDIRECT_INFO structure. */
-
- while (info_file[indirect_top] != info_separator_char &&
- index < MAX_INDIRECT_FILES)
- {
- indirect_top = forward_lines (1, indirect_top);
- if (info_file[indirect_top] == info_separator_char)
- break;
-
- /* Ignore blank lines. */
- if (info_file[indirect_top] == '\n')
- continue;
-
- #ifdef MSDOS
- #if 0 /* never */
- sscanf (&info_file[indirect_top], "%s%ld",
- temp_filename, &temp_first_byte);
- #endif /* never */
- /* For some obscure reason, Microsoft's sscanf ()
- looses from time to time, typically for the first
- node in a large tag table. I don't pretent to
- understand this, but I have a fix. */
- replace_loosing_sscanf (&info_file[indirect_top],
- temp_filename, &temp_first_byte);
-
- #else /* not MSDOS */
- sscanf (&info_file[indirect_top], "%s%d",
- temp_filename, &temp_first_byte);
- #endif /* not MSDOS */
-
- if (strlen (temp_filename))
- {
- temp_filename[strlen (temp_filename) - 1] = '\0';
- indirect_list[index].filename =
- (char *) xmalloc (strlen (temp_filename) + 1);
- strcpy (indirect_list[index].filename, temp_filename);
- indirect_list[index].first_byte = temp_first_byte;
- index++;
- }
- }
- /* Terminate the table. */
- if (index == MAX_INDIRECT_FILES)
- {
- display_error ("Sorry, the INDIRECT file array isn't large enough.");
- index--;
- }
- indirect_list[index].filename = (char *) NULL;
- }
- indirect = true;
- }
- }
- }
- return (true);
- }
-
- boolean
- get_node (filename, nodename, popping)
- char *nodename, *filename;
- boolean popping;
- {
- /* Make current_info_node be NODENAME. This could involve loading
- a file, etc. POPPING is true if we got here because we are popping
- one level. */
-
- LONG pointer;
- char internal_filename[FILENAME_LEN];
- char internal_nodename[NODENAME_LEN];
-
- if (nodename && *nodename)
- {
- /* Maybe nodename looks like: (filename)nodename, or worse: (filename).
- If so, extract the stuff out. */
- if (*nodename == '(')
- {
- /* We have a filename at the start of the nodename.
- Find out what it is. */
-
- int temp = 1;
- int temp1 = 0;
- char character;
-
- filename = internal_filename;
- while ((character = nodename[temp]) && character != ')')
- {
- filename[temp - 1] = character;
- temp++;
- }
- filename[temp - 1] = '\0';
-
- /* We have the filename now. The nodename follows. */
- internal_nodename[0] = '\0';
- if (nodename[temp])
- {
- temp++;
- while (internal_nodename[temp1++] = nodename[temp++]);
- }
- nodename = internal_nodename;
- }
- }
-
- if (!popping)
- push_node (current_info_file, current_info_node, pagetop, nodetop);
-
-
- if (!nodename || !*nodename)
- {
- nodename = internal_nodename;
- strcpy (nodename, "Top");
- }
-
- if (!filename || !*filename)
- {
- filename = internal_filename;
- strcpy (filename, current_info_file);
- }
-
- if (!*filename)
- strcpy (filename, "dir");
-
- if (!get_info_file (filename, true))
- goto node_not_found;
-
- if (strcmp (nodename, "*") == 0)
- {
- /* The "node" that we want is the entire file. */
- pointer = 0;
- goto found_node;
- }
-
- /* If we are using a tag table, see if we can find the nodename in it. */
- if (tag_table)
- {
- pointer = find_node_in_tag_table (nodename, 0);
- if (pointer < 1)
- {
- /* Then the search through the tag table failed. Maybe
- we should try searching the buffer? Nahh, just barf. */
- boolean pop_node ();
- node_not_found:
- if (popping)
- return (false); /* second time through. */
- display_error
- ("Sorry, unable to find the node \"%s\" in the file \"%s\".", nodename, filename);
- current_info_file[0] = '\0';
- current_info_node[0] = '\0';
- last_loaded_info_file[0] = '\0';
- pop_node (internal_filename, internal_nodename, &nodetop, &pagetop);
- get_node (internal_filename, internal_nodename, true);
- return (false);
- }
-
- /* Otherwise, the desired location is right here.
- Scarf the position byte. */
- while (tag_table[pointer] != '\177')
- pointer++;
- #ifdef MSDOS
- #if 0 /* never */
- sscanf (&tag_table[pointer + 1], "%ld", &pointer);
- #endif /* never */
- /* It may be paranoia ... (cf. replace_loosing_sscanf()) */
- pointer = atol (&tag_table[pointer + 1]);
- #else /* not MSDOS */
- sscanf (&tag_table[pointer + 1], "%d", &pointer);
- #endif /* not MSDOS */
-
- /* Okay, we have a position pointer. If this is an indirect file,
- then we should look through the indirect_list for the first
- element.first_byte which is larger than this. Then we can load
- the specified file, and win. */
- if (indirect)
- {
- /* Find the filename for this node. */
- int index;
- for (index = 0; index < MAX_INDIRECT_FILES &&
- indirect_list[index].filename != (char *) NULL; index++)
- {
- if (indirect_list[index].first_byte > pointer)
- {
- /* We found it. */
- break;
- }
- }
- if (!get_info_file (indirect_list[index - 1].filename, true))
- goto node_not_found;
- pointer -= indirect_list[index - 1].first_byte;
-
- /* Here is code to compensate for the header of an indirect file. */
- {
- LONG tt = find_node_start (0);
- if (tt > -1)
- pointer += tt;
- }
- }
- else
- {
- /* This tag table is *not* indirect. The filename of the file
- containing this node is the same as the current file. The
- line probably looks like:
- File: info, Node: Checking25796 */
- }
- }
- else
- {
- /* We don't have a tag table. The node can only be found by
- searching this file in its entirety. */
- get_info_file (filename, true);
- pointer = 0;
- }
-
- /* Search this file, using pointer as a good guess where to start. */
- /* This is the same number that RMS used. It might be right or wrong. */
- pointer -= 1000;
- if (pointer < 0)
- pointer = 0;
-
- pointer = find_node_in_file (nodename, pointer);
- if (pointer < 0)
- goto node_not_found;
-
- /* We found the node in it's file. Remember exciting information. */
-
- found_node:
- back_lines (0, pointer);
- nodetop = pagetop = pointer;
- strcpy (current_info_node, nodename);
- strcpy (current_info_file, filename);
- get_node_extent ();
- return (true);
- }
-
- /* Get the bounds for this node. NODETOP points to the start of the
- node. Scan forward looking for info_separator_char, and remember
- that in NODEBOT. */
- VOID
- get_node_extent ()
- {
- LONG index = nodetop;
- int character;
- boolean do_it_till_end = (strcmp (current_info_node, "*") == 0);
-
- nodelines = 0;
-
- again:
- while ((index < info_buffer_len) &&
- ((character = info_file[index]) != info_separator_char))
- {
- if (character == '\n')
- nodelines++;
- index++;
- }
- if (do_it_till_end && index != info_buffer_len)
- {
- index++;
- goto again;
- }
- nodebot = index;
- }
-
- /* Locate the start of a node in the current search_buffer. Return
- the offset to the node start, or minus one. START is the place in
- the file at where to begin the search. */
- LONG
- find_node_start (start)
- LONG start;
- {
- return (search_forward (start_of_node_string, start));
- }
-
- /* Find NODENAME in TAG_TABLE. */
- LONG
- find_node_in_tag_table (nodename, offset)
- char *nodename;
- LONG offset;
- {
- LONG temp;
-
- set_search_constraints (tag_table, tag_buffer_len);
-
- temp = offset;
- while (true)
- {
- offset = search_forward (NODE_ID, temp);
- if (offset < 0)
- return (offset);
- temp = skip_whitespace (offset + strlen (NODE_ID));
- if (strnicmp (tag_table + temp, nodename, strlen (nodename)) == 0)
- if (*(tag_table + temp + strlen (nodename)) == '\177')
- return (temp);
- }
- }
-
- /* Find NODENAME in INFO_FILE. */
- LONG
- find_node_in_file (nodename, offset)
- char *nodename;
- LONG offset;
- {
- LONG temp;
-
- set_search_constraints (info_file, info_buffer_len);
-
- while (true)
- {
- offset = find_node_start (offset);
- if (offset < 0)
- return (offset);
- else
- temp = forward_lines (1, offset);
- if (temp == offset)
- return (-1); /* At last line now, just a node start. */
- else
- offset = temp;
- temp = string_in_line (NODE_ID, offset);
- if (temp > -1)
- {
- temp = skip_whitespace (temp + strlen (NODE_ID));
- if (strnicmp (info_file + temp, nodename, strlen (nodename)) == 0)
- {
- int check_exact = *(info_file + temp + strlen (nodename));
-
- if (check_exact == '\t' ||
- check_exact == ',' ||
- check_exact == '.' ||
- check_exact == '\n')
- return (offset);
- }
- }
- }
- }
-
- #ifdef MSDOS
- /* Nomen est omen ... */
- void
- replace_loosing_sscanf (char *buf, char *str, long *np)
- {
- while (*buf && !isspace (*buf))
- *str++ = *buf++;
- *str = '\0';
- *np = atol (buf);
- }
- #endif /* not MSDOS */
-
-
- /* **************************************************************** */
- /* */
- /* Dumping and Printing Nodes */
- /* */
- /* **************************************************************** */
-
- /* Make a temporary filename based on STARTER and the PID of this Info. */
- char *
- make_temp_filename (starter)
- char *starter;
- {
- #ifdef MSDOS /* make a digestable filename... */
- char *temp = (char *) xmalloc (strlen (starter) + 1);
- char *p = temp;
- while (*starter)
- {
- *p++ = isalnum (*starter) ? *starter : '-';
- starter++;
- }
- *p = '\0';
- #else /* not MSDOS */
- char *temp = (char *) xmalloc (FILENAME_LEN);
- sprintf (temp, "%s-%d", starter, getpid ());
- #endif /* not MSDOS */
- return (temp);
- }
-
- /* Delete a file. Print errors if necessary. */
- deletefile (filename)
- char *filename;
- {
- if (unlink (filename) != 0)
- return (file_error (filename));
- else
- return (0);
- }
-
- #ifndef MSDOS
-
- /* Print the contents of FILENAME using lpr. */
- char *print_command = "lpr -p ";
-
- printfile (filename)
- char *filename;
- {
- int length = strlen (print_command) + strlen (filename) + strlen ("\n") + 1;
- char *command = (char *) xmalloc (length);
- int error;
-
- display_error ("Printing file `%s'...\n", filename);
- sprintf (command, "%s%s", print_command, filename);
- error = system (command);
- if (error)
- display_error ("Can't invoke `%s'", command);
- free (command);
- return (error);
- }
-
- #endif /* not MSDOS */
-
- /* Dump the current node into a file named FILENAME.
- Return 0 if the dump was successful, otherwise,
- print error and exit. */
- dump_current_node (filename)
- char *filename;
- {
- LONG i = nodetop;
- int c;
- FILE *output_stream = fopen (filename, "w");
- if (output_stream == (FILE *) NULL)
- return (file_error (filename));
-
- while (i < nodebot && i < info_buffer_len)
- {
- c = info_file[i];
- if (c < 0x020 && !(index ("\n\t\f", c)))
- {
- putc ('^', output_stream);
- }
- if (putc (info_file[i++], output_stream) == EOF)
- {
- fclose (output_stream);
- return (file_error (filename));
- }
- }
- fclose (output_stream);
- return (0);
- }
-
- /* **************************************************************** */
- /* */
- /* Toplevel eval loop. */
- /* */
- /* **************************************************************** */
-
- #define MENU_HEADER "\n* Menu:"
- #define MENU_ID "\n* "
- #define FOOTNOTE_HEADER "*Note"
-
- /* Number of items that the current menu has. */
- int the_menu_size = 0;
-
- /* The node that last made a menus completion list. */
- #ifdef MSDOS
- char menus_nodename[NODENAME_LEN]; /* this should be correct for GNU too... */
- char menus_filename[NODENAME_LEN];
- #else /* not MSDOS */
- char *menus_nodename[NODENAME_LEN];
- char *menus_filename[NODENAME_LEN];
- #endif /* not MSDOS */
-
- boolean window_bashed = false;
-
- char *visible_footnote = 0; /* The default prompt string for the Follow Reference command. */
- VOID
- toploop ()
- {
- boolean done = false;
- boolean inhibit_display = false;
- int command;
- char nodename[NODENAME_LEN];
-
- while (!done)
- {
-
- if (!inhibit_display || window_bashed)
- display_page ();
- inhibit_display = window_bashed = false;
-
- nodename[0] = '\0'; /* Don't display old text in input line. */
-
- goto_xy (echo_area.left, echo_area.top);
- command = blink_cursor ();
- clear_echo_area ();
-
- if (command == EOF)
- {
- done = true;
- continue;
- }
- command = to_upper (command);
-
- switch (command)
- {
-
- case 'D':
- get_node ((char *) NULL, "(dir)Top", false);
- break;
-
- case 'H':
- if ((the_window.bottom - the_window.top) < 24)
- get_node ((char *) NULL, "(info)Help-Small-Screen", false);
- else
- get_node ((char *) NULL, "(info)Help", false);
- break;
-
- case 'N':
- if (!next_node ())
- {
- display_error ("No NEXT for this node!");
- inhibit_display = true;
- }
- break;
-
- case 'P':
- if (!prev_node ())
- {
- display_error ("No PREV for this node!");
- inhibit_display = true;
- }
- break;
-
- case 'U':
- if (!up_node ())
- {
- display_error ("No UP for this node!");
- inhibit_display = true;
- }
- break;
-
- case 'M':
- if (!build_menu ())
- {
- display_error ("No menu in this node!");
- inhibit_display = true;
- break;
- }
-
- if (!readline ("Menu item: ", nodename, NODENAME_LEN, true))
- {
- clear_echo_area ();
- inhibit_display = true;
- break;
- }
-
- I_goto_xy (echo_area.left, echo_area.top);
- if (!find_menu_node (nodename, nodename))
- {
- display_error ("\"%s\" is not a menu item!", nodename);
- inhibit_display = true;
- break;
- }
-
- if (get_node ((char *) NULL, nodename, false))
- clear_echo_area ();
- break;
-
-
- case 'F':
- {
- char footnote[NODENAME_LEN];
- if (!build_notes ())
- {
- display_error ("No cross-references in this node!");
- inhibit_display = true;
- break;
- }
- strcpy (footnote, visible_footnote);
- if (!readline ("Follow reference: ", footnote, NODENAME_LEN, true))
- {
- inhibit_display = true;
- break;
- }
-
- I_goto_xy (echo_area.left, echo_area.top);
- if (!find_note_node (footnote, nodename))
- {
- display_error ("\"%s\" is not a cross-reference in this node!", footnote);
- inhibit_display = true;
- break;
- }
-
- if (get_node ((char *) NULL, nodename, false))
- clear_echo_area ();
- break;
- }
-
- case 'L':
- {
- char filename[FILENAME_LEN], nodename[NODENAME_LEN];
- LONG ptop, ntop;
- if (pop_node (filename, nodename, &ntop, &ptop) &&
- get_node (filename, nodename, true))
- {
- pagetop = ptop;
- }
- else
- inhibit_display = true;
- break;
- }
-
- case SPACE:
- if (!next_page ())
- {
- display_error ("At last page of this node now!");
- inhibit_display = true;
- }
- break;
-
- case DELETE:
- if (!prev_page ())
- {
- display_error ("At first page of this node now!");
- inhibit_display = true;
- }
- break;
-
- case 'B':
- if (pagetop == nodetop)
- {
- display_error ("Already at beginning of this node!");
- inhibit_display = true;
- }
- else
- pagetop = nodetop;
- break;
-
- /* I don't want to do this this way, but the documentation clearly states
- that '6' doesn't work. It states this for a reason, and ours is not to
- wonder why... */
-
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- {
- int item = command - '0';
- if (!build_menu ())
- {
- display_error ("No menu in this node!");
- inhibit_display = true;
- break;
- }
- if (item > the_menu_size)
- {
- display_error ("There are only %d items in the menu!",
- the_menu_size);
- inhibit_display = true;
- break;
- }
-
- if (!get_menu (item))
- inhibit_display = true;
- break;
- }
-
- case 'G':
- if (!readline ("Goto node: ", nodename, NODENAME_LEN, false))
- {
- inhibit_display = true;
- break;
- }
- clear_echo_area ();
- get_node ((char *) NULL, nodename, false);
- break;
-
- case 'S':
- {
- /* Search from the starting position forward for a string.
- Of course, this should really wrap the search around, but
- it doesn't do that yet. Select the node containing the
- desired string. Put the top of the page screen_lines/2
- lines behind it, but not before nodetop. */
-
- extern LONG info_buffer_len;
- LONG pointer, temp;
- LONG search_start = pagetop;
-
- if (!readline ("Search for string: ", nodename, NODENAME_LEN, false))
- {
- inhibit_display = true;
- break;
- }
-
- I_goto_xy (echo_area.left, echo_area.top);
-
- if (indirect)
- {
- /* Put the indirect search right here. */
- display_error ("This is an indirect file, and I can't search these yet!");
- break;
- }
- else
- {
- set_search_constraints (info_file, info_buffer_len);
- pointer = search_forward (nodename, search_start);
- if (pointer == -1)
- {
- display_error ("\"%s\" not found! Try another info file.", nodename);
- inhibit_display = true;
- break;
- }
- }
- temp = search_backward (start_of_node_string, pointer);
- if (temp == -1)
- temp = search_forward (start_of_node_string, pointer);
- if (temp == -1)
- {
- brians_error ();
- break;
- }
-
- search_start = pointer;
- pointer = forward_lines (1, temp);
- if (!extract_field ("Node:", nodename, pointer))
- {
- display_error ("There doesn't appear to be a nodename for this node.");
- get_node ((char *) NULL, "*", false);
- pagetop = pointer;
- break;
- }
-
- if (get_node ((char *) NULL, nodename, false))
- clear_echo_area ();
-
- /* Make the string that the user wanted be visible, and
- centered in the screen. */
- {
- pointer = back_lines ((the_window.bottom - the_window.top) / 2, search_start);
- if (pointer < nodetop)
- pointer = nodetop;
-
- pagetop = pointer;
- }
-
- break;
- }
-
- case CTRL ('H'):
- case '?':
- help_use_info ();
- break;
-
- case 'Q':
- done = true;
- break;
-
- case CTRL ('L'): /* Control-l is redisplay */
- break;
-
- case '(': /* You *must* be trying to type a complete nodename. */
- strcpy (nodename, "(");
- if (!readline ("Goto node: ", nodename, NODENAME_LEN, false))
- {
- inhibit_display = true;
- clear_echo_area ();
- break;
- }
- I_goto_xy (echo_area.left, echo_area.top);
- if (get_node ((char *) NULL, nodename, false))
- clear_echo_area ();
- break;
-
- case CTRL ('P'):
- /* Print the contents of this node on the default printer. We
- would like to let the user specify the printer, but we don't
- want to ask her each time which printer to use. Besides, he
- might not know, which is why it (the user) is in the need of
- Info. */
- {
- char *tempname = make_temp_filename (current_info_node);
- #ifdef MSDOS
- if (dump_current_node (tempname) == 0)
- display_error ("Dumped node to `%s'.\n", tempname);
- #else /* not MSDOS */
- if (dump_current_node (tempname) == 0 &&
- printfile (tempname) == 0 &&
- deletefile (tempname) == 0)
- {
- display_error ("Printed node. Go pick up your output.\n");
- }
- #endif /* not MSDOS */
- inhibit_display = true;
- free (tempname);
- }
- break;
-
- default:
- inhibit_display = true;
- display_error ("Unknown command! Press '?' for help.");
-
- }
- }
- }
-
- VOID
- help_use_info ()
- {
- /* Tell this person how to use Info. */
-
- open_typeout ();
- print_string ("\n\
- Commands in Info\n\
- \n\
- h Invoke the Info tutorial.\n\
- \n\
- Selecting other nodes:\n\
- n Move to the \"next\" node of this node.\n\
- p Move to the \"previous\" node of this node.\n\
- u Move \"up\" from this node.\n\
- m Pick menu item specified by name.\n\
- Picking a menu item causes another node to be selected.\n\
- f Follow a cross reference. Reads name of reference.\n\
- l Move to the last node you were at.\n\
- \n\
- Moving within a node:\n\
- Space Scroll forward a page.\n\
- DEL Scroll backward a page.\n\
- b Go to the beginning of this node.\n\
- \n\
- Advanced commands:\n\
- q Quit Info.\n\
- 1 Pick first item in node's menu.\n\
- 2 - 5 Pick second ... fifth item in node's menu.\n\
- g Move to node specified by name.\n\
- You may include a filename as well, as (FILENAME)NODENAME.\n\
- s Search through this Info file for a specified string,\n\
- and select the node in which the next occurrence is found.\n\
- Ctl-p Print the contents of this node using `lpr -p'.\n\
- \n\
- Done.\n\n");
- close_typeout ();
- }
-
- boolean
- next_node ()
- {
- /* Move to the node specified in the NEXT field. */
-
- char nodename[NODENAME_LEN];
-
- if (!extract_field ("Next:", nodename, nodetop))
- return (false);
- return (get_node ((char *) NULL, nodename, false));
- }
-
- boolean
- prev_node ()
- {
- /* Move to the node specified in the PREVIOUS field. */
-
- char nodename[NODENAME_LEN];
-
- if (!extract_field ("Previous:", nodename, nodetop)
- && !extract_field ("Prev:", nodename, nodetop))
- return (false);
- return (get_node ((char *) NULL, nodename, false));
- }
-
- boolean
- up_node ()
- {
- /* Move to the node specified in the UP field. */
-
- char nodename[NODENAME_LEN];
-
- if (!extract_field ("Up:", nodename, nodetop))
- return (false);
- return (get_node ((char *) NULL, nodename, false));
- }
-
- /* Build a completion list of menuname/nodename for each
- line in this node that is a menu item. */
- boolean
- build_menu ()
- {
- LONG pointer = nodetop;
- char menuname[NODENAME_LEN];
- char nodename[NODENAME_LEN];
-
- if (strcmp (menus_nodename, current_info_node) == 0 &&
- strcmp (menus_filename, current_info_file) == 0)
- return (the_menu_size != 0);
-
- strcpy (menus_nodename, current_info_node);
- strcpy (menus_filename, current_info_file);
- free_completion_list ();
-
- set_search_constraints (info_file, nodebot);
- if ((pointer = search_forward (MENU_HEADER, nodetop)) < 0)
- #ifdef MSDOS
- {
- /* We have *not* build a completion list! */
- menus_filename[0] = menus_nodename[0] = '\0';
- return (false);
- }
- #else /* not MSDOS */
- return (false);
- #endif /* not MSDOS */
-
- /* There is a menu here. Look for members of it. */
- pointer += strlen (MENU_HEADER);
-
- while (true)
- {
- int index;
-
- pointer = search_forward (MENU_ID, pointer);
- if (pointer < 0)
- break; /* no more menus in this node. */
- pointer = (skip_whitespace (pointer + strlen (MENU_ID)));
- index = 0;
- while ((menuname[index] = info_file[pointer]) && menuname[index] != ':')
- {
- index++, pointer++;
- }
- menuname[index] = '\0';
- pointer++;
- if (info_file[pointer] == ':')
- {
- strcpy (nodename, menuname);
- }
- else
- {
- pointer = skip_whitespace (pointer);
- index = 0;
- while ((nodename[index] = info_file[pointer]) &&
- nodename[index] != '\t' &&
- nodename[index] != '.' &&
- nodename[index] != ',')
- {
- index++, pointer++;
- }
- nodename[index] = '\0';
- }
- add_completion (menuname, nodename);
- the_menu_size++;
- }
- if (the_menu_size)
- completion_list = reverse_list (completion_list);
- #ifdef MSDOS
- else
- /* We have *not* build a completion list! */
- menus_filename[0] = menus_nodename[0] = '\0';
- #endif /* MSDOS */
-
- return (the_menu_size != 0);
- }
-
- boolean
- get_menu (item)
- int item;
- {
- /* Select ITEMth item from the list built by build_menu. */
-
- if (!build_menu ())
- return (false);
- if (item > the_menu_size)
- return (false);
- else
- {
- COMP_ENTRY *temp = completion_list;
- while (--item && temp)
- temp = temp->next;
- return (get_node ((char *) NULL, temp->data, false));
- }
- }
-
- boolean
- find_menu_node (string, nodename)
- char *string, *nodename;
- {
- /* Scan through the ?already? built menu list looking
- for STRING. If you find it, put the corresponding nodes
- name in NODENAME. */
- return (scan_list (string, nodename));
- }
-
- boolean
- scan_list (string, nodename)
- char *string, *nodename;
- {
- /* The work part of find_menu_node and find_note_node. */
- COMP_ENTRY *temp = completion_list;
-
- while (temp)
- {
- if (strnicmp (string, temp->identifier, strlen (string)) == 0)
- {
- strcpy (nodename, temp->data);
- return (true);
- }
- temp = temp->next;
- }
- return (false);
- }
-
- VOID
- clean_up (string)
- char *string;
- {
- /* Remove <CR> and whitespace from string, replacing them with
- only one space. Exception: <CR> at end of string disappears. */
-
- char *to = string;
- char last_char = 0;
- boolean result;
-
- while (*to = *string++)
- {
- if (*to == '\n')
- {
- *to = SPACE;
- if (!(*(to + 1)))
- {
- *to = '\0';
- return;
- }
- }
- result = (last_char == SPACE);
- last_char = *to;
- if (last_char != SPACE)
- to++;
- else if (!result)
- to++;
- }
- }
-
- LONG
- find_footnote_ref (from)
- LONG from;
- {
- /* Find a reference to "*Note". Return the offset of
- the start of that reference, or -1. */
-
- while (true)
- {
- from = search_forward (FOOTNOTE_HEADER, from);
- if (from < 0)
- return (from);
- else
- from += strlen (FOOTNOTE_HEADER);
- if (info_file[from] == ' ' ||
- info_file[from] == '\n' ||
- info_file[from] == '\t')
- return (from);
- }
- }
-
-
- boolean
- build_notes ()
- {
- /* Build an array of (footnote.nodename) for each footnote in this node. */
-
- LONG pointer;
- char notename[NODENAME_LEN];
- char nodename[NODENAME_LEN];
-
- set_search_constraints (info_file, nodebot);
-
- if ((find_footnote_ref (nodetop)) < 0)
- return (false);
- pointer = nodetop;
-
- menus_filename[0] = menus_nodename[0] = '\0';
- visible_footnote = "";
- free_completion_list ();
-
- while (true)
- {
- int index;
-
- pointer = find_footnote_ref (pointer);
- if (pointer < 0)
- break; /* no more footnotes in this node. */
-
-
- pointer = skip_whitespace_and_cr (pointer);
- index = 0;
- while ((notename[index] = info_file[pointer]) && notename[index] != ':')
- {
- index++, pointer++;
- }
- notename[index] = '\0';
- clean_up (notename);
- pointer++;
- if (info_file[pointer] == ':')
- {
- strcpy (nodename, notename);
- }
- else
- {
- pointer = skip_whitespace (pointer);
- index = 0;
- while ((nodename[index] = info_file[pointer]) &&
- nodename[index] != '\t' &&
- nodename[index] != '.' &&
- nodename[index] != ',')
- {
- index++, pointer++;
- }
- nodename[index] = '\0';
- }
- /* Add the notename/nodename to the list. */
- add_completion (notename, nodename);
- the_menu_size++;
-
- /* Remember this identifier as the default if it is the first one in the
- page. */
- if (!(*visible_footnote) &&
- pointer > pagetop &&
- pointer < forward_lines (the_window.bottom - the_window.top, pointer))
- visible_footnote = completion_list->identifier;
- }
- if (the_menu_size)
- completion_list = reverse_list (completion_list);
- return (the_menu_size != 0);
- }
-
- boolean
- find_note_node (string, nodename)
- char *string, *nodename;
- {
- /* Scan through the ?already? built footnote list looking
- for STRING. If found, place the corresponding node name
- in NODENAME. */
-
- return (scan_list (string, nodename));
- }
-
- /*===(cut here)===*/
-
- /* Include the rest: */
-
- #include "info.d"
-
-
- /*
- * Local Variables:
- * mode:C
- * ChangeLog:ChangeLog
- * End:
- */
-